package com.fudan.es.pack;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNested;
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket;

import com.fudan.es.Aggs;
import com.fudan.es.entity.FieldMeta;
import com.fudan.es.entity.Metric;
import com.fudan.es.entity.Metric.MetricType;

public class TermAggsPack extends BucketAggsPack{

	private AggregationBuilder termAggs;
	private AggregationBuilder rootAggs;

	private FieldMeta field;
	public TermAggsPack(FieldMeta field,int dimensionSize) {
		this.field = field;
		this.path = field.getNestedPath();
		termAggs = Aggs.terms(field.getName(), field.getName(),dimensionSize);
		reverseAggs = new HashMap<>();
		if (field.getInNested()) {
			setRootAggs(Aggs.nested(path, field.getNestedPath()).subAggregation(termAggs));
			ReverseNestedAggregationBuilder rootReverse = AggregationBuilders.reverseNested("root");
			termAggs.subAggregation(rootReverse);
			reverseAggs.put("root", rootReverse);
			String[] pathLevel = path.split("[.]");
			String rp = null;
			for (int i = 0; i < pathLevel.length; i++) {
				if (i == 0) {
					rp = pathLevel[0];
				} else {
					rp = rp + "." + pathLevel[i];
				}
				ReverseNestedAggregationBuilder pReverse = AggregationBuilders.reverseNested(rp).path(rp);
				termAggs.subAggregation(pReverse);
				reverseAggs.put(rp, pReverse);
			}
		} else {
			setRootAggs(termAggs);
		}
		setMetricList(new ArrayList<>());
	}

	public TermAggsPack addMetric(Metric metric) {
		FieldMeta field = metric.getField();
		switch (metric.getType()) {
		case SUM: {
			FieldMeta countField = metric.getField();
			if(countField!=null) {
				if (path == null) {

					if (countField.getInNested()) {
						termAggs.subAggregation(Aggs.nested(metric.getType()+"$"+field.getNestedPath(), field.getNestedPath())
								.subAggregation(Aggs.sum(metric.getType()+"$"+countField.getName(), countField.getName())));
					}else {
						termAggs.subAggregation(Aggs.sum(metric.getType()+"$"+countField.getName(), countField.getName()));
					}
				} else {

					if (countField.getInNested()) {
						//在同一级
						if(path.equals(countField.getNestedPath())) {
							termAggs.subAggregation(Aggs.sum(metric.getType()+"$"+countField.getName(), countField.getName()));
						//不在同一级
						}else {
							AggregationBuilder reversePack = reverseAggs.get(countField.getNestedPath());
							//在同一个路径上
							if (reversePack != null) {
								reversePack.subAggregation(Aggs.sum(metric.getType()+"$"+countField.getName(), countField.getName()));
							//访问需要通过根路径
							} else {
								reverseAggs.get("root")
										.subAggregation(Aggs.nested(metric.getType()+"$"+countField.getNestedPath(), countField.getNestedPath())
												.subAggregation(Aggs.sum(metric.getType()+"$"+countField.getName(), countField.getName())));
							}
						}
					//查找的是非nested节点
					} else {
						reverseAggs.get("root").subAggregation(Aggs.sum(metric.getType()+"$"+countField.getName(), countField.getName()));
					}
				}
			}
			break;
		}
		case AVG: {
			FieldMeta countField = metric.getField();
			if(countField!=null) {
				if (path == null) {
					if (countField.getInNested()) {
						termAggs.subAggregation(Aggs.nested(metric.getType()+"$"+field.getNestedPath(), field.getNestedPath())
								.subAggregation(Aggs.avg(metric.getType()+"$"+countField.getName(), countField.getName())));
					}else {
						termAggs.subAggregation(Aggs.avg(metric.getType()+"$"+countField.getName(), countField.getName()));
					}
				} else {
					if (countField.getInNested()) {
						if(path.equals(countField.getNestedPath())) {
							termAggs.subAggregation(Aggs.avg(metric.getType()+"$"+countField.getName(), countField.getName()));
						}else {
							AggregationBuilder reversePack = reverseAggs.get(countField.getNestedPath());
							if (reversePack != null) {
								reversePack.subAggregation(Aggs.avg(metric.getType()+"$"+countField.getName(), countField.getName()));
							} else {
								reverseAggs.get("root")
										.subAggregation(Aggs.nested(metric.getType()+"$"+countField.getNestedPath(), countField.getNestedPath())
												.subAggregation(Aggs.avg(metric.getType()+"$"+countField.getName(), countField.getName())));
							}
						}
					} else {
						reverseAggs.get("root").subAggregation(Aggs.avg(metric.getType()+"$"+countField.getName(), countField.getName()));
					}
				}
			}
			break;
		}
		case COUNT: {
			FieldMeta countField = metric.getField();
			if(countField!=null) {
				if (path == null) {
					if (countField.getInNested()) {
						termAggs.subAggregation(Aggs.nested(metric.getType()+"$"+field.getNestedPath(), field.getNestedPath())
								.subAggregation(Aggs.terms(metric.getType()+"$"+countField.getName(), countField.getName())));
					}else {
						termAggs.subAggregation(Aggs.terms(metric.getType()+"$"+countField.getName(), countField.getName()));
					}
				} else {
					if (countField.getInNested()) {
					   if(path.equals(countField.getNestedPath())) {
							AggregationBuilder subTerms = Aggs.terms(metric.getType()+"$"+countField.getName(), countField.getName());
							if(reverseAggs!=null) {
								Set<String> set = reverseAggs.keySet();
								for (String reverseKey : set) {
									subTerms.subAggregation(reverseAggs.get(reverseKey));
								}
							}
							termAggs.subAggregation(subTerms);
						}else {
							AggregationBuilder reversePack = reverseAggs.get(countField.getNestedPath());
							if (reversePack != null) {
								reversePack.subAggregation(Aggs.terms(metric.getType()+"$"+countField.getName(), countField.getName()));
							} else {
								Set<String> keySet = reverseAggs.keySet();
								for (String key : keySet) {
									reverseAggs.get(key)
									.subAggregation(Aggs.nested(metric.getType()+"$"+countField.getNestedPath(), countField.getNestedPath())
											.subAggregation(Aggs.terms(metric.getType()+"$"+countField.getName(), countField.getName())));
								}
							}
						}
					} else {
						reverseAggs.get("root").subAggregation(Aggs.terms(metric.getType()+"$"+countField.getName(), countField.getName()));
					}
				}
			}
			break;
		}
		default:
			break;
		}
		metricList.add(metric);
		return this;
	}

	public List<Map<String,Object>> getTermResponse(SearchResponse response) {
		List<Map<String,Object>> list = new ArrayList<>();
		Terms terms = null;
		if(this.field.getInNested()) {
			Nested nested = response.getAggregations().get(field.getNestedPath());
			terms = nested.getAggregations().get(field.getName());
		}else {
			terms = response.getAggregations().get(field.getName());
		}
		List<? extends Bucket> buckets = terms.getBuckets();
		for (Bucket bucket : buckets) {
			Map<String,Object> bucketMap = new HashMap<>();
			String key = bucket.getKeyAsString();
			Long val = bucket.getDocCount();
			bucketMap.put("field", key);
			bucketMap.put("count", val);
			fetchRootMetric(bucket,bucketMap);
			if(reverseAggs!=null) {
				Set<String> keySet = reverseAggs.keySet();
				for (String nk : keySet) {
					Map<String,Object> reverseBucketMap = new HashMap<>();
					ReverseNested reverseNested = bucket.getAggregations().get(nk);
					fetchReverseNestedMetric(reverseNested,reverseBucketMap);
					Long docCount = reverseNested.getDocCount();
					reverseBucketMap.put("field", nk);
					reverseBucketMap.put("count", docCount);
					bucketMap.put(nk, reverseBucketMap);
				}
			}
			getBucketReverse(bucket,bucketMap);
			list.add(bucketMap);
		}
		return list;
	}

	public void getBucketReverse(Bucket bucket,Map<String,Object> bucketMap) {
		for (Metric metric : metricList) {
			FieldMeta metricField = metric.getField();
			if(metricField == null) return;
			if(metric.getType() == MetricType.COUNT) {
				if(reverseAggs!=null) {
					Set<String> keySet = reverseAggs.keySet();
					Terms terms = null;
					if(metricField.getInNested()) {
						if(field.getInNested()) {
							if(metricField.getNestedPath().equals(field.getNestedPath())) {
								terms = bucket.getAggregations().get(metric.getType()+"$"+metricField.getName());
								if(terms == null) return;
								List<? extends Bucket> subBucket = terms.getBuckets();
								for (Bucket sub : subBucket) {
									for (String nk : keySet) {
										if(bucketMap.containsKey(nk)) {
											Map<String,Object> nmap = (Map<String, Object>) bucketMap.get(nk);
											if(nmap != null &&nmap.containsKey(metricField.getName())) {
												Map<String,Object> fmap = (Map<String, Object>) nmap.get(metricField.getName());
												if(fmap!= null && fmap.containsKey("doc")) {
													List<Map<String,Object>> list = (List<Map<String, Object>>) fmap.get("doc");
													ReverseNested n = sub.getAggregations().get(nk);
													Map<String, Object> map = new HashMap<>();
													map.put("field", sub.getKeyAsString());
													map.put("count", n.getDocCount());
													list.add(map);
												}
											}
										}
									}
								}
							}else {
								for (String nk : keySet) {
									if(nk.equals("root")) continue;
									ReverseNested res = bucket.getAggregations().get(nk);
									Nested nested = res.getAggregations().get(metric.getType()+"$"+metricField.getNestedPath());
									Terms t = nested.getAggregations().get(metric.getType()+"$"+metricField.getName());
									List<? extends Bucket> buckets = t.getBuckets();
									for (Bucket b : buckets) {
										Map<String, Object> map = new HashMap<>();
										map.put("field", b.getKeyAsString());
										map.put("count", b.getDocCount());
										if(bucketMap.containsKey(nk)) {
											Map<String,Object> nmap = (Map<String, Object>) bucketMap.get(nk);
											if(nmap != null &&nmap.containsKey(metricField.getName())) {
												Map<String,Object> fmap = (Map<String, Object>) nmap.get(metricField.getName());
												if(fmap!= null && fmap.containsKey("doc")) {
													List<Map<String,Object>> list = (List<Map<String, Object>>) fmap.get("doc");
													list.add(map);
												}
											}
										}
									}
								}
							}
						}else {

						}
					}else {
						if(field.getInNested()) {
						}else {

						}
					}

				}
			}
		}
	}

	public AggregationBuilder getRootAggs() {
		return rootAggs;
	}

	public void setRootAggs(AggregationBuilder rootAggs) {
		this.rootAggs = rootAggs;
	}
}
